/*
 *  Copyright (C) 2010 takaji
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * SwingDialog.java
 *
 * Created on Jan 21, 2010, 11:27:51 PM
 */
package org.dakside.duck.dakwizard;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.border.TitledBorder;

/**
 *
 * @author takaji
 */
public class WizardDialog extends javax.swing.JDialog {

    public static final int FINISH = 0x1;
    public static final int CANCEL = 0x2;
    public static final int BACK = 0x4;
    public static final int NEXT = 0x8;
    public static final int STEP_ACTION = 0xF;
    public static final int STATUS_ERROR = 0x10;
    public static final int STATUS_FINISHED = 0x11;
    public static final int STATUS_CANCELLED = 0x12;
    public static final int STATUS_NEW = 0x14;
    private WizardModel model = null;

    /** Creates new form SwingDialog */
    public WizardDialog(java.awt.Frame parent, WizardModel model) {
        super(parent, true);
        initComponents();
        if (model != null) {
            this.model = model;
            this.model.addModelListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    refresh();
                }
            });
            refresh();
        } else {
            throw new IllegalArgumentException("Model cannot be null");
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        panelNavigator = new javax.swing.JPanel();
        btnCancel = new javax.swing.JButton();
        btnFinish = new javax.swing.JButton();
        btnNext = new javax.swing.JButton();
        btnBack = new javax.swing.JButton();
        panelView = new javax.swing.JScrollPane();

        setTitle("Dak Wizard");
        setLocationByPlatform(true);
        setModal(true);

        panelNavigator.setBorder(javax.swing.BorderFactory.createTitledBorder("Dak wizard"));

        btnCancel.setMnemonic('C');
        btnCancel.setText("Cancel");
        btnCancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnCancelActionPerformed(evt);
            }
        });

        btnFinish.setMnemonic('F');
        btnFinish.setText("Finish");
        btnFinish.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnFinishActionPerformed(evt);
            }
        });

        btnNext.setMnemonic('N');
        btnNext.setText("Next");
        btnNext.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnNextActionPerformed(evt);
            }
        });

        btnBack.setMnemonic('B');
        btnBack.setText("Back");
        btnBack.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnBackActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout panelNavigatorLayout = new javax.swing.GroupLayout(panelNavigator);
        panelNavigator.setLayout(panelNavigatorLayout);
        panelNavigatorLayout.setHorizontalGroup(
            panelNavigatorLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelNavigatorLayout.createSequentialGroup()
                .addContainerGap(129, Short.MAX_VALUE)
                .addComponent(btnBack, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnNext, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnFinish, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(btnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        panelNavigatorLayout.setVerticalGroup(
            panelNavigatorLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(panelNavigatorLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(panelNavigatorLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btnCancel, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
                    .addComponent(btnFinish, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
                    .addComponent(btnNext, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE)
                    .addComponent(btnBack, javax.swing.GroupLayout.DEFAULT_SIZE, 27, Short.MAX_VALUE))
                .addContainerGap())
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(panelNavigator, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(panelView, javax.swing.GroupLayout.DEFAULT_SIZE, 539, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(panelView, javax.swing.GroupLayout.DEFAULT_SIZE, 303, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(panelNavigator, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void btnBackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnBackActionPerformed
        WizardStep step = model.getCurrentStep();
        if (step.isAllowed(BACK)) {
            model.getCurrentStep().doAction(BACK);
        }
    }//GEN-LAST:event_btnBackActionPerformed

    private void btnNextActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNextActionPerformed
        // TODO: Should call back the step to update data before proceed
        WizardStep step = model.getCurrentStep();
        if (step.isAllowed(NEXT)) {
            model.getCurrentStep().doAction(NEXT);
        }
    }//GEN-LAST:event_btnNextActionPerformed

    private void btnFinishActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFinishActionPerformed
        WizardStep step = model.getCurrentStep();
        if (step.isFinishable()) {
            model.getCurrentStep().doAction(FINISH);
            this.setVisible(false);
        }
    }//GEN-LAST:event_btnFinishActionPerformed

    private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed
        WizardStep step = model.getCurrentStep();
        if (step.isCancellable()) {
            model.getCurrentStep().doAction(CANCEL);
            this.setVisible(false);
        }
    }//GEN-LAST:event_btnCancelActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnBack;
    private javax.swing.JButton btnCancel;
    private javax.swing.JButton btnFinish;
    private javax.swing.JButton btnNext;
    private javax.swing.JPanel panelNavigator;
    private javax.swing.JScrollPane panelView;
    // End of variables declaration//GEN-END:variables

    /**
     * Refresh dialog
     */
    private void refresh() {
        if (this.model == null || this.model.getCurrentStep() == null) {
            throw new RuntimeException("Invalid wizard model");
        }
        WizardStep currentStep = this.model.getCurrentStep();
        //reset view if needed
        Component c = (Component) currentStep.getView();
        if (!this.contains(c)) {
            //clear view
            this.panelView.getViewport().removeAll();
            this.panelView.getViewport().add(c);
            if (currentStep.requireFocus()) {
                c.requestFocus();
            } else {
                this.focus(currentStep);
            }
            currentStep.onShow();
        } else {
            this.focus(currentStep);
        }

        //reset button states
        btnBack.setEnabled(currentStep.isAllowed(BACK));
        btnNext.setEnabled(currentStep.isAllowed(NEXT));
        btnFinish.setEnabled(currentStep.isFinishable());
        btnCancel.setEnabled(currentStep.isCancellable());
    }

    /**
     * Focus on action button <br/>
     * If can finish then focus on finish button<br/>
     * Else if can go next then focus no next button
     * @param currentStep
     */
    private void focus(WizardStep currentStep) {
        if (currentStep == null) {
            //do nothing?
        } else if (currentStep.isAllowed(FINISH)) {
            btnFinish.requestFocus();
        } else if (currentStep.isAllowed(NEXT)) {
            btnNext.requestFocus();
        }
    }

    /**
     * Check if this integer is a status indicator
     * @param status
     * @return
     */
    public static boolean isStatus(int status) {
        return status == STATUS_NEW
                || status == STATUS_ERROR
                || status == STATUS_FINISHED
                || status == STATUS_CANCELLED;
    }

    //<editor-fold defaultstate="collapsed" desc="Customize dialog">
    public void setNavigatorTitle(String title) {
        TitledBorder tb = (TitledBorder) this.panelNavigator.getBorder();
        tb.setTitle(title);
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc="Dialog function">
    /**
     * Show this wizard dialog
     * @return this model object
     */
    public Object showDialog() {
        if (this.getParent() != null) {
            this.setLocationRelativeTo(this.getParent());
        }
        this.setVisible(true);
        this.dispose();
        return this.model;
    }

    /**
     * Show a wizard dialog with the specified model<br/>
     * The title will be "Wizard"</br>
     * and the navigator title will be "Wizard"
     * @param parent
     * @param model
     * @return this wizard model
     */
    public static Object showDialog(Frame parent, WizardModel model) {
        return showDialog(parent, model, "Wizard", "Wizard", null);
    }

    /**
     * Show a wizard dialog with the specified model<br/>
     * The title will be "Wizard"</br>
     * and the navigator title will be "Navigator"
     * @param model
     * @return this wizard model
     */
    public static Object showDialog(WizardModel model) {
        return showDialog(null, model, "Wizard", "Navigator", null);
    }

    public static Object showDialog(WizardModel model, String title) {
        return showDialog(null, model, title, "Navigator", null);
    }

    /**
     * Show a wizard dialog with the specified model
     * @param parent
     * @param model
     * @param title the title of the dialog
     * @param navigatorText the title of the navigator box
     * @return this wizard model
     */
    public static Object showDialog(Frame parent, WizardModel model, String title, String navigatorText, Dimension d) {
        WizardDialog dialog = new WizardDialog(parent, model);
        dialog.setTitle(title);
        dialog.setNavigatorTitle(navigatorText);
        if (d != null) {
            dialog.setSize(d);
        }
        return dialog.showDialog();
    }

    public static Object showDialog(WizardModel model, String title, String navigatorText) {
        return showDialog(null, model, title, navigatorText, null);
    }

    private boolean contains(Component c) {
        for (int i = 0; i < this.panelView.getViewport().getComponentCount(); i++) {
            if (this.panelView.getViewport().getComponent(i) == c) {
                return true;
            }
        }
        return false;
    }
    //</editor-fold>
}
